The presentation first makes the case for modularity in modern JavaScript systems and the resulting need for a transitive dependency management solution. Later it covers the state of dependency management in JavaScript. Finally it describes the open-source Jingo JavaScript dependency manager (http://jingo.googlecode.com) and its approach to solving the dependency management problem.
1. +0
JS
1 0 0 1 0 1 0
0 1 0 1 0 0 1 1
0 1 0 0 0 1 0 0
DM
0 1 0 0 1 1 0 1
JavaScript Dependency Management
Breaking Down the Barriers to Modular JavaScript Systems
Sean M. Duncan
2. +
Sean M. Duncan
Senior Software Consultant with Solution Design Group
9 years of experience working with Web applications
Primarily Enterprise Java space with a recent focus on
dynamic languages
Carol.com (Groovy/Grails)
Giftag.com (Python/Django/Google App Engine)
Giftag Firefox Addon (JavaScript)
J S
D M
3. +
My Reintroduction to JavaScript
I’m a fairly new believer in the potential for elegant and
maintainable large-scale JavaScript systems
While working on the Giftag Firefox addon I started to see a
side of JavaScript that had escaped me before
Douglas Crockford would call it “The Good Parts”. I call it
“Professional-Grade JavaScript”
J S
D M
4. +
JavaScript’s Sordid Past
Small tasks in limited contexts
Writing systems that behaved consistently across popular
browsers was difficult
JavaScript’s scoping confused developers due to
inconsistency with other languages that share a C-like syntax
Debugging support for JavaScript was severely lacking
Testing and verification tools were either not available
or still in an early state of evolution
J S
D M
5. +
JavaScript’s Sorted Present
Rich Internet Applications Demand More
Cross-browser consistency is now much easier to achieve
Quality of JavaScript literature has drastically improved
JavaScript: The Good Parts by Douglas Crockford
Pro JavaScript Techniques by John Resig
High-quality, general-purpose frameworks are now widely
available
Browser vendors and extension developers have made and
continue to make strides in the area of debugging tools
J S
Quality testing and verification tools are now available
D M
6. +
Professional-Grade JavaScript
Modern JavaScript requires the same discipline and organization we employ with other platforms
Focus on the parts of the language that exhibit consistency
and steer clear of those that don’t (see JSLint)
Practice Unobtrusive JavaScript by separating server-side
data, client-side logic and content markup
Understand functional scoping and closure
Strive to have minimal impact on the global namespace
Verify the proper function of your code (see jqUnit/jqMock)
Decompose your system into loosely coupled, purpose-
focused modules
J S
Organize your project source in a manner that caters
to intuition
D M
7. +
The Case for Modular JavaScript
Proven approach for other platforms that scale well to large
bodies of code
Java, Python, Ruby and Groovy
Loosely coupled systems are easier to enhance and maintain
Purpose-focused modules are easier to verify and reuse
Lends itself well to both object-oriented and functional
solutions (see Python)
Enables us to divide and conquer larger problems
J S
through layers of abstraction
D M
8. +
The Elements of Modularity
Logical Modularity:
The problem domain concepts are captured in discrete software
artifacts
These software artifacts must have minimal knowledge of other
components within the system
Components work together at different layers of abstraction to
accomplish the software’s goal
Physical Modularity:
File system resources are organized parallel to the concepts
within the problem domain
Easiest to accomplish when file system structures mirror
J S
logical structures
D M
9. JavaScript Makes Modularity Difficult+
JavaScript as a language facilitates modularity; JavaScript as a platform stands in the way
Developers must manually enter script tags to load modules
Direct and transitive dependencies must be explicitly
declared
Tags must be arranged in transitive dependency order
Pages become tightly coupled to modules they should
need no knowledge of
Manual dependency management is initially tedious,
and maintenance is worse
J S
D M
10. Transitive Dependency Management+
Modules declare their direct dependencies and the module
loader/manager takes it from there
We couldn’t imagine Java, Python, Groovy or Ruby without it
Why not JavaScript?
See JavaScript’s Past (small tasks)
An important tool that should be in our Professional-Grade
JavaScript toolbox
J S
D M
11. +
First-Generation Approaches
Script Tag Append
A dependent module declares a dependency with something
1.
like module.require(‘foo’);
The dependency manager adds a <script src=“foo.js”/>
2.
element to the Document Object Model (DOM)
Ajax and Eval
A dependent module declares a dependency with something
1.
like module.require(‘foo’);
The dependency manager makes an Ajax request for the
2.
module with xhr.open(‘GET’, ‘foo.js’, false);
send(null); …
J S
The dependency manager loads the response text by
3.
calling window.eval(xhr.responseText);
D M
12. +
Script Tag Append
Pros:
Simple to implement
Standard caching behavior
Source code is properly attributed to its file resource
Cons:
The timing of the response to the script element introduction is
inconsistent from one browser to the next
Dependent modules may be interpreted before their
dependencies
J S
D M
13. +
Ajax and Eval
Pros:
Still relatively simple to implement
Standard caching behavior
Source can be loaded synchronously, making transitive
dependency order easy to guarantee
Cons:
The use of window.eval(xhr.responseText) causes source to be
attributed to the module manager rather than the module’s file
resource
Some browsers have been known to ignore the asynch flag
in the xhr.open(method, url, asynch) call
J S
D M
14. +
Dojo Toolkit’s Module Loader
Ajax and Eval with Global Callback
Translates module names into file resources in a manner
similar to Java packages
Registered ’onLoad’ callbacks are invoked when all pending
modules are loaded
Originally designed to streamline the distribution
of the Dojo Toolkit rather than to be a general solution
J S
D M
15. +
Dojo Toolkit Code Examples
Publish a module:
dojo.provide(‘my.module’);
Create a module:
dojo.declare(‘my.module’, null, {
// module body
});
Declare a dependency:
dojo.require(‘your.module’);
Run some dependent code:
dojo.addOnLoad(function() {
// code that depends on ‘your.module’
J S
});
D M
16. +
YUI Loader
Script Tag Append with Availability Polling and Callbacks
Feature-rich but biased toward visual components
Can be used for CSS as well as JavaScript
Rollup feature loads bundles of related modules in one file
Availability determination is complex and inconsistent
across browsers
“Nothing can be done … except to pause and hope for the best.”
Availability Polling:
YUI loader polls for a variable that non-YUI scripts publish
J
S
when they are interpreted
D M
17. +
YUI Loader Code Examples
Publish a module to the loader:
loader.addModule({
name: ‘colorpicker’,
type: ‘js’,
varName: ‘colorPickerLoaded’, // availability polling
fullPath: ‘http://www.example.com/js/module.js’,
requires: [‘module.a’, ‘module.b’]
});
Configure the loader and pull in some dependencies:
var loader = new YAHOO.util.YUILoader({
require: [quot;colorpickerquot;, quot;treeviewquot;],
onSuccess: function() {
// code that depends on colorpicker and treeview
},
J S
combine: true // allow “rollup” of required modules
});
loader.insert();
D M
18. +
jQuery & Prototype
Ajax and Eval / Basic Script GET
Neither really trying to be a player in the dependency
management space
jQuery:
$.getScript(‘scripts/foo/bar/baz.js’, function() {
// code that requires ‘foo.bar.baz’
});
Prototype:
var getScript = function(url, callback) {
new Ajax.Request(url, {
method: ‘get’,
onSuccess: function(transport) {
J S
window.eval(transport);
callback();
}
});
D M
};
19. +
IN
J GO
Jingo JavaScript Dependency Manager
jingo.googlecode.com
Breaking Down the Barriers to Modular JavaScript Systems
John Bailey
Sean M. Duncan
20. +
Jingo
Created to solve the problem of JavaScript dependency
management in a lightweight, framework-agnostic package
Avoids technical limitations and framework bias of common
approaches to JavaScript dependency management
Streamlines both the development and maintenance of
modular JavaScript systems
J S
D M
21. +
How Jingo Works
Works within the constraints of browser processes by
extending the script lifecycle
Combines Script Tag Append and Module Body Callbacks to
make modules available in transitive dependency order
Module Resolution advances modules through a progression
of three distinct states
J S
D M
22. +
The Module Lifecycle: Loading
Translates module names into file resources in a manner
similar to Java packages
Script tag with its ‘src’ attribute set to the translated
resource path is appended to the DOM
J S
D M
23. +
The Module Lifecycle: Declared
Browser loads the source for a given module, interprets the
file, and executes the jingo.declare call
Jingo registers the module’s body callback and ensures all
direct dependencies are transitioned into loading state
J S
D M
24. +
The Module Lifecycle: Resolved
Modules with no dependencies are immediately resolved
Modules are resolved by calling the registered body
callback
Jingo continues to scan for declared modules ready for
resolution
Process repeats until all declared modules and their
dependencies have been resolved
J S
Module Body Callbacks are guaranteed to be invoked
in transitive dependency order
D M
25. +
Visualizing the Module Lifecycle
- Dependency Tree - - Page Source -
<script src=“jingo.js”/>
<script>
jingo.anonymous({
require: [‘B’, ‘C’],
exec: function() {
// module A
}
});
</script>
J S
D M
26. +
Visualizing the Module Lifecycle
- Dependency Tree - - Page Source -
<script src=“jingo.js”/>
<script>...</script>
<script src=“B.js”/>
<script src=“C.js”/>
J S
D M
27. +
Visualizing the Module Lifecycle
- Dependency Tree - - Page Source -
<script src=“jingo.js”/>
<script>...</script>
<script src=“B.js”/>
<script src=“C.js”/>
<script src=“D.js”/>
<script src=“E.js”/>
J S
D M
28. +
Visualizing the Module Lifecycle
- Dependency Tree - - Page Source -
<script src=“jingo.js”/>
<script>...</script>
<script src=“B.js”/>
<script src=“C.js”/>
<script src=“D.js”/>
<script src=“E.js”/>
<script src=“F.js”/>
J S
D M
29. +
Visualizing the Module Lifecycle
- Dependency Tree - - Page Source -
<script src=“jingo.js”/>
<script>...</script>
<script src=“B.js”/>
<script src=“C.js”/>
<script src=“D.js”/>
<script src=“E.js”/>
<script src=“F.js”/>
<script src=“G.js”/>
<script src=“H.js”/>
J S
D M
30. +
Getting Started with Jingo
Initializing Jingo
Jingo’s main repository defaults to ‘scripts’, its logging
verbosity defaults to ‘warn’ and its script loading timeout
defaults to 30 seconds
If the defaults work for your project, initialization is
unnecessary
Override the default main repository and logging verbosity:
jingo.init({
repos: {
main: ‘../scripts’
},
verbosity: ’debug’,
J S
timeout: 10000
});
D M
31. +
Getting Started with Jingo
A Simple Reusable Module
Declare a single module that doesn’t require any other
modules to do its work:
jingo.declare({
name: 'Greeter’,
as: function() {
Greeter = function() {
this.welcome = function(name) {
alert('Hello ' + name + ', how are you?');
};
};
}
});
J S
D M
32. +
Getting Started with Jingo
A Namespaced Reusable Module
Unfortunately, the previous example pollutes the global
namespace
Introduce a namespace called hallmart:
jingo.declare({
name: ’hallmart.Greeter’,
as: function() {
hallmart.Greeter = function() {
this.welcome = function(name) {
alert('Hello ' + name + ', how are you?');
};
};
}
});
J S
Jingo ensures the existence of the enclosing namespace
for us
D M
33. +
Getting Started with Jingo
A Reusable Module with Nested Namespacing
This pattern works for arbitrarily deep namespaces as well
Create an iteration module nested within a utilities package:
jingo.declare({
name: ‘hallmart.util.iterators’,
as: function() {
hallmart.util.iterators = {
each: function(data, closure) {
for(key in data) {
if(data.hasOwnProperty(key) {
closure(key, data[key]);
}
}
};
J S
};
}
});
D M
34. +
Getting Started with Jingo
A Reusable Module with Dependencies
Modules only have to declare their direct dependencies
Declare a dependent module:
jingo.declare({
require: [
‘hallmart.Greeter’
],
name: ‘hallmart.Store’,
as: function() {
hallmart.Store = function() {
var greeter = new hallmart.Greeter();
this.admit = function(customer) {
greeter.welcome(customer.name);
};
J S
};
}
});
D M
35. +
Getting Started with Jingo
Declaring an Anonymous Module
<html>
...
<script type=“text/javascript” src=“scripts/jingo.js”></script>
<script type=“text/javascript”>
jingo.anonymous({
require: [‘hallmart.util.iterators’, ‘hallmart.Store’],
exec: function() {
var each = hallmart.util.iterators.each;
var customers = [{name: ‘Bil’}, {name: ‘Ted’}];
var store = new hallmart.Store();
each(customers, function(index, customer) {
store.admit(customer);
};
}
});
J
</script>
S
...
</html>
D M
36. +
Getting Started with Jingo
Rollup Module for Unobtrusive JavaScript
jingo.declare({
require: [‘hallmart.util.iterators’, ‘hallmart.Store’],
name: ‘hallmart.controller.foo’,
as: function() {
var each = hallmart.util.iterators.each;
var customers = [{name: ‘Bil’}, {name: ‘Ted’}];
var store = new hallmart.Store();
each(customers, function(index, customer) {
store.admit(customer);
};
}
});
...
<script type=“text/javascript” src=“scripts/jingo.js”/>
J
<script type=“text/javascript”>
S
jingo.anonymous({ require: [‘hallmart.controller.foo’] });
</script>
...
D M
37. +
Getting Started with Jingo
Cross-Project Reuse via Multiple Module Repositories
Modularity creates opportunities for reuse beyond the
confines of a single project:
jingo.init({
repos: {
mib: ‘http://mib.security.com/scripts’
}
});
Require modules from multiple repositories:
jingo.declare({
require: [
‘mib:security.guard’,
‘hallmart.Greeter’
],
J S
name: ‘hallmart.Store’,
as: function() {
// module body
}
D M
});
38. +
Distinguishing Features
Cross-browser consistency
Firefox, Internet Explorer, Safari, Chrome, Opera
Small footprint
Download size < 6KB
Increased module load efficiency may lead to net decrease in
overall network traffic
Standard caching
Jingo module source is cached in the same manner as source
loaded via manual script tag entry
J S
Debugging support
Source code is attributed to the appropriate file resource
D M
39. +
Distinguishing Features, continued
Framework-agnostic
Focused, unbiased, standalone JavaScript dependency solution
Scope assurance
Module Body Callbacks guarantee the presence of a non-global
enclosing scope in the body of every Jingo module
Namespace assurance
Jingo automatically ensures the existence of enclosing
namespaces for namespaced modules
Intuitive project structure
J S
File system organization mirrors the logical module
structure
D M
40. +
Ideas for the Future
Central script repository
jingo.init({
repos: {
jingo: ‘http://repos.jingo-js.com/scripts’
}
});
jingo.declare({
require: [ ‘jingo:foo.bar.baz’ ],
...
});
Jingo Server-Side
Same syntax as Jingo but server-side process builds complete
dependency graph rollup and returns it as one file
J S
Server-side caching of assembled script rollups
Any suggestions?
D M
41. +
Thanks Everyone
Particularly:
Refactr (hosting)
Solution Design Group (pizza!)
J S
D M
42. +
Live Code Demo with John
jingo.googlecode.com
Sean Duncan sean@artisanlogic.com
John Bailey john@artisanlogic.com
J S
D M
43. +
Questions?
jingo.googlecode.com
Sean Duncan sean@artisanlogic.com
John Bailey john@artisanlogic.com
J S
D M
44. +
General-Purpose Frameworks
jQuery (www.jquery.com)
Prototype (www.prototypejs.com)
Dojo Toolkit (www.dojotoolkit.com)
ExtJS (www.extjs.com)
YUI (developer.yahoo.com/yui/)
Archetype (www.archetypejs.org)
…
J S
D M
45. +
Testing and Verification Tools
Unit Testing
jqUnit (jqunit.googlecode.com)
JsUnit (www.jsunit.net)
Mock Objects
jqMock (jqmock.googlecode.com)
JSMock (jsmock.sourceforge.net)
JSCoverage (siliconforks.com/jscoverage/)
JSLint (www.jslint.com)
J S
Selenium (seleniumhq.org)
D M
46. +
Other Valuable Tools
Compaction
JSMin (www.crockford.com/javascript/jsmin.html)
Packer (dean.edwards.name/packer/)
Dojo ShrinkSafe (dojotoolkit.org/docs/shrinksafe)
YUI Compressor (developer.yahoo.com/yui/compressor/)
Embedded Documentation
JSDoc Toolkit (code.google.com/p/jsdoc-toolkit/)
J S
D M